﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using Pixysoft.Framework.Reflection;
using System.Reflection;
using Pixysoft.Framework.Verifications;
using Pixysoft.Json;

namespace Pixysoft.Framework.Jsons
{
    /// <summary>
    /// 400 Bad Request
    /// 403 Forbidden
    /// 404 Not Found
    /// 500 Internal Server Error
    /// 503 Service Unavailable
    /// 
    /// http://topic.csdn.net/u/20081105/16/3f617b8b-e952-45e7-99ab-f430e36f13ad.html
    /// 不调用Response.end();
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class JsonPage<T> : Page where T : IJsonHandler
    {
        /// <summary>
        /// 支持post/get模式
        /// </summary>
        /// <param name="e"></param>
        protected override void OnLoad(EventArgs e)
        {
            // create object instance

            Type objectType = typeof(T);

            object targetOBject = CreateTargetObject(objectType);

            if (targetOBject == null)
            {
                throwError(string.Format("initialize controller fail. type:{0}", objectType.Name));

                return;
            }

            // original request

            if (IsOriginalRequest())
            {
                (targetOBject as IJsonHandler).OnLoad(e);

                base.OnLoad(e);

                return;
            }


            // get method name

            string methodName = Request.QueryString[StringHelper.param_method];

            if (string.IsNullOrEmpty(methodName))
            {
                throwError("No method name available in Json Request. please check the QueryString[.jmethod]");

                return;
            }


            // get querystring

            Dictionary<string, string> queryStrings = new Dictionary<string, string>();

            if (IsPostRequest())
            {
                queryStrings = CreatePostFormString();
            }
            else
            {
                queryStrings = CreateGetQueryString();
            }

            // create method info

            MethodInfo method = GetMethod(objectType, methodName, queryStrings.Count);

            if (method == null)
            {
                throwError(string.Format("method not founded on server side controller. type:{0} method:{1} paracount:{2}",
                    objectType.Name, methodName, queryStrings.Count));

                return;
            }

            // initialize parameters

            List<object> parameters = new List<object>();

            try
            {
                parameters = InitializeParameters(method, queryStrings);
            }
            catch (Exception ex)
            {
                if (ex.InnerException != null)
                    throwError(ex.InnerException.Message);
                else
                    throwError(ex.Message);

                return;
            }


            // check method is from org

            if (!method.DeclaringType.Name.Equals(objectType.Name))
            {
                objectType = method.DeclaringType;

                targetOBject = CreateTargetObject(objectType);
            }

            // log request

            LogRequest(method, queryStrings, parameters);


            //invoke method

            object returnValue = null;

            if (method.ReturnType.Equals(typeof(void)))
            {
                method.Invoke(targetOBject, parameters.ToArray());//directly call no catch

                OnResponse(null);

                return;
            }
            else
            {
                bool hasException = false;

                string message = null;

                try
                {
                    returnValue = method.Invoke(targetOBject, parameters.ToArray());
                }
                catch (Exception ex)
                {
                    //2010-11-19 这里导致了threadabortexception.

                    hasException = true;

                    LoggerHelper.Fatal(ex);

                    if (ex.InnerException != null)
                        message = ex.InnerException.ToString();
                    else
                        message = ex.ToString();
                }

                if (hasException)
                {
                    throwError(string.Format("invoke json method fail. type:{0} method:{1} exception:{2}", objectType.Name, methodName, message));

                    return;
                }
            }

            // return json value.

            if (returnValue == null)
            {
                OnResponse(null);

                return;
            }

            if (method.ReturnType.IsValueType)
            {
                OnResponse(returnValue.ToString());

                return;
            }

            switch (Pixysoft.Tools.ParserHelper.GetDataTypeByType(method.ReturnType))
            {
                case DotNetDataType.Object:
                case DotNetDataType.UNKNOWN:
                    {
                        OnResponse(Pixysoft.Json.MyJson.Instance.Serialize(returnValue));

                        return;
                    }
                case DotNetDataType.Byte:
                    {
                        throwError(string.Format("json request do not support byte[] response for controller. type:{0} method:{1}", objectType.Name, methodName));

                        return;
                    }
                default:
                    {
                        OnResponse(returnValue.ToString());

                        return;
                    }
            }

        }

        /// <summary>
        /// 是否原生态的HTTP请求
        /// </summary>
        /// <returns></returns>
        private bool IsOriginalRequest()
        {
            if (!Request.QueryString.HasKeys())
                return true;

            foreach (string key in Request.QueryString.Keys)
            {
                if (string.Equals(key, StringHelper.param_method))
                    return false;
            }

            return true;
        }

        /// <summary>
        /// 判断是否POST请求
        /// </summary>
        /// <returns></returns>
        private bool IsPostRequest()
        {
            if (Request.Form.Keys.Count == 0)
                return false;

            return true;
        }

        /// <summary>
        /// 获取get请求参数
        /// </summary>
        /// <returns></returns>
        private Dictionary<string, string> CreateGetQueryString()
        {
            Dictionary<string, string> list = new Dictionary<string, string>();

            foreach (string key in Request.QueryString.Keys)
            {
                if (string.Equals(key, StringHelper.param_method))
                    continue;

                if (string.IsNullOrEmpty(key))
                    continue;

                string ukey = key.Trim().ToUpper();

                if (list.ContainsKey(ukey))
                    continue;

                list.Add(ukey, Request.QueryString[key]);
            }

            return list;
        }

        /// <summary>
        /// 获取post请求参数
        /// </summary>
        /// <returns></returns>
        private Dictionary<string, string> CreatePostFormString()
        {
            Dictionary<string, string> list = new Dictionary<string, string>();

            foreach (string key in Request.Form.Keys)
            {
                if (string.Equals(key, StringHelper.param_method))
                    continue;

                if (string.IsNullOrEmpty(key))
                    continue;

                string ukey = key.Trim().ToUpper();

                if (list.ContainsKey(ukey))
                    continue;

                list.Add(ukey, Request.Form[key]);
            }

            return list;
        }

        /// <summary>
        /// 反射获取方法内容
        /// </summary>
        /// <param name="type"></param>
        /// <param name="methodName"></param>
        /// <param name="parameterCount"></param>
        /// <returns></returns>
        private MethodInfo GetMethod(Type type, string methodName, int parameterCount)
        {
            foreach (MethodInfo method in type.GetMethods())
            {
                if (!string.Equals(method.Name, methodName))
                    continue;

                if (method.GetParameters().Length != parameterCount)
                    continue;

                return method;
            }

            foreach (Type iType in type.GetInterfaces())
            {
                if (!iType.Name.Contains("IJsonReferense"))
                    continue;

                try
                {
                    Type referenceType = iType.GetGenericArguments()[0];

                    MethodInfo referenceMethod = GetMethod(referenceType, methodName, parameterCount);

                    if (referenceMethod != null)
                        return referenceMethod;
                }
                catch (Exception ex)
                {
                    LoggerHelper.Fatal(ex);
                    continue;
                }
            }

            return null;

        }

        /// <summary>
        /// 反射创建对象
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private object CreateTargetObject(Type type)
        {
            ConstructorInfo constructorInfo =
                type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
                null, new Type[] { }, null);

            if (constructorInfo == null)
            {
                return null;
            }

            return constructorInfo.Invoke(null);

        }

        /// <summary>
        /// 初始化调用参数
        /// </summary>
        /// <param name="method"></param>
        /// <param name="queryStrings"></param>
        /// <returns></returns>
        private List<object> InitializeParameters(MethodInfo method, Dictionary<string, string> queryStrings)
        {
            List<object> parameters = new List<object>();

            foreach (ParameterInfo parameter in method.GetParameters())
            {
                string parameterName = parameter.Name.Trim().ToUpper();

                if (!queryStrings.ContainsKey(parameterName))
                    throw Exceptions.UnexpectedResultException("missing parameter name:{0} in method:{1}, type:{2}", parameterName, method.Name, method.DeclaringType.Name);

                string parameterValue = queryStrings[parameterName];

                if (string.IsNullOrEmpty(parameterValue))
                {
                    parameters.Add(Pixysoft.Tools.ParserHelper.GetNullValue(parameter.ParameterType));
                }
                else
                {
                    object result = null;

                    switch (Pixysoft.Tools.ParserHelper.GetPropertyType(parameter.ParameterType))
                    {
                        case DotNetPropertyType.ValueList:
                        case DotNetPropertyType.ValueArray:
                            {
                                result = Pixysoft.Json.MyJson.Instance.DeSerialize(parameter.ParameterType, parameterValue);

                                break;
                            }
                        case DotNetPropertyType.Object:
                            {
                                result = ParseJsonObject(parameter.ParameterType, parameterValue);

                                break;
                            }
                        default:
                            {
                                if (!Pixysoft.Tools.ParserHelper.TryParse(parameter.ParameterType, parameterValue, out result, null))
                                {
                                    throw Exceptions.UnexpectedResultException("parse parameter fail. method:{0} name:{1} type:{2} value:{3}",
                                        method.Name, parameter.Name, parameter.ParameterType.Name, parameterValue);
                                }

                                break;
                            }

                    }


                    parameters.Add(result);
                }
            }

            return parameters;
        }

        private object ParseJsonObject(Type type, string parameterValue)
        {
            throw Exceptions.UnexpectedResultException("not support parse json object yet.");
        }

        private void LogRequest(MethodInfo method, Dictionary<string, string> queryStrings, List<object> parameters)
        {
            LoggerHelper.Debug("JSON::{0}.{1}", method.DeclaringType.Name, method.Name);

            int index = 0;

            foreach (string key in queryStrings.Keys)
            {
                LoggerHelper.Debug("::{0} {1} {2}", key, queryStrings[key], parameters[index++]);
            }
        }

        private void throwError(string content)
        {
            LoggerHelper.Error(content);

            LoggerHelper.Error("request:{0}", Request.RawUrl);

            //throw Exceptions.UnexpectedResultException(content);

            Response.Clear();
            if (string.IsNullOrEmpty(content))
            {
                Response.Write(0);
            }
            else
            {
                content = content.Replace("\r\n", "\n").Replace("\n\n", "\n");
                Response.Write(content);
            }
            Response.StatusCode = 500;
            Response.Flush();
            Response.End();
            //Context.ApplicationInstance.CompleteRequest();
        }

        private void OnResponse(string content)
        {
            LoggerHelper.Debug(content);

            Response.Clear();
            if (string.IsNullOrEmpty(content))
            {
                Response.Write(0);
            }
            else
            {
                Response.Write(content);
            }
            Response.StatusCode = 200;
            Response.Flush();
            Response.End();
            //Context.ApplicationInstance.CompleteRequest();
        }

    }
}
